﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace T.FAS.Runtime.Base.UnitTests.CodeCoverage.T_FAS_Base_SGPlugin.Helper
{
    public class EntityGenerator
    {
        public const int STR_LENGTH = 100;
        public const int DECIMAL_MAX_VALUE = 100;
        public const int INT_MAX_VALUE = 5000;
        public const int FLOAT_MAX_VALUE = 5000;
        public const int DOUBLE_MAX_VALUE = 700000;
        public const int LONG_MAX_VALUE = 700000;
        public const int SHORT_MAX_VALUE = 5000;

        public static List<T> GenerateData<T>(int num)
        {
            List<T> list = new List<T>();
            if (num > 0)
            {
                for (int i = 0; i < num; i++)
                {
                    list.Add(GenerateData<T>());
                }
            }
            return list;
        }

        public static T GenerateData<T>()
        {
            Type t = typeof(T);
            Type str = typeof(string);
            try
            {
                object obj = Activator.CreateInstance(t);
                var properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                                  .Where(p => p.PropertyType.IsValueType || str.IsAssignableFrom(p.PropertyType))
                                  .ToList();
                var fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                              .Where(p => p.FieldType.IsValueType || str.IsAssignableFrom(p.FieldType))
                              .ToList();
                foreach (var prop in properties)
                {
                    if (prop.CanWrite)
                    {
                        var val = GenerateValue(prop.PropertyType, GetStrInfo(prop));
                        prop.SetValue(obj, val);
                    }
                }
                foreach (var field in fields)
                {
                    var val = GenerateValue(field.FieldType, GetStrInfo(field));
                    field.SetValue(obj, val);
                }
                return (T)obj;
            }
            catch (Exception e)
            {
                throw;
            }
        }

        private static object GenerateValue(Type type, StrFieldInfo strFieldInfo = null, string prefix = "")
        {
            if (string.IsNullOrEmpty(prefix))
                prefix = "test";
            var strPrefix = $"{prefix}_{strFieldInfo?.Name}_";
            int strLength = strFieldInfo == null ? INT_MAX_VALUE : strFieldInfo.Length;
            Random random = new Random();
            if (type == typeof(string))
            {
                if (strFieldInfo != null && strFieldInfo.IsKey && strFieldInfo.Length >= 36)
                    return Guid.NewGuid().ToString();
                if (strLength > strPrefix.Length)
                    return $"{strPrefix}{Str(strLength - strPrefix.Length, false)}";
                else
                    return Str(strLength - strPrefix.Length, false);
            }
            else
            {
                //可空类型
                if (type.IsGenericType && type.GetGenericTypeDefinition().FullName.Contains(typeof(Nullable).ToString()))
                {
                    Type normalType = type.GetGenericArguments()[0];
                    var flag = random.Next(0, 2) == 1 ? true : false;
                    if (flag)
                        return GetValueTypeVal(normalType);
                    return null;
                }
                if (type.IsValueType)
                {
                    return GetValueTypeVal(type);
                }
            }
            return default;
        }

        private static object GetValueTypeVal(Type type)
        {
            Random random = new Random();
            if (type == typeof(bool))
            {
                return random.Next(0, 2) == 1 ? true : false;
            }
            else if (type == typeof(decimal))
            {
                return random.Next(0, DECIMAL_MAX_VALUE);
            }
            else if (type == typeof(double))
            {
                return random.Next(0, DOUBLE_MAX_VALUE);
            }
            else if (type == typeof(int))
            {
                return random.Next(0, INT_MAX_VALUE);
            }
            else if (type == typeof(float))
            {
                return random.Next(0, FLOAT_MAX_VALUE);
            }
            else if (type == typeof(long))
            {
                return random.Next(0, LONG_MAX_VALUE);
            }
            else if (type == typeof(short))
            {
                return random.Next(0, SHORT_MAX_VALUE);
            }
            else if (type == typeof(DateTime))
            {
                return DateTime.Now;
            }
            else
            {
                return Activator.CreateInstance(type);
            }
        }

        private static StrFieldInfo GetStrInfo(object obj)
        {
            if (obj is PropertyInfo p)
            {
                if (p.PropertyType != typeof(string))
                    return default;
                else
                {
                    StrFieldInfo info = new StrFieldInfo();
                    info.Name = p.Name;
                    int strLength = STR_LENGTH;
                    //var keyAttr = p.GetCustomAttribute(typeof(KeyAttribute)) as KeyAttribute;
                    //var strAttr = p.GetCustomAttribute(typeof(StringLengthAttribute)) as StringLengthAttribute;
                    //if (strAttr != null && strAttr.MaximumLength > 0)
                    //{
                    //    strLength = strAttr.MaximumLength;
                    //}
                    info.Length = strLength;
                    //if (keyAttr != null)
                    //{
                    //    info.IsKey = true;
                    //}
                    return info;
                }
            }
            else if (obj is FieldInfo f)
            {
                if (f.FieldType != typeof(string))
                {
                    return default;
                }
                else
                {
                    StrFieldInfo info = new StrFieldInfo();
                    info.Name = f.Name;
                    int strLength = STR_LENGTH;
                    //var keyAttr = f.GetCustomAttribute(typeof(KeyAttribute)) as KeyAttribute;
                    //var strAttr = f.GetCustomAttribute(typeof(StringLengthAttribute)) as StringLengthAttribute;
                    //if (strAttr != null && strAttr.MaximumLength > 0)
                    //{
                    //    strLength = strAttr.MaximumLength;
                    //}
                    info.Length = strLength;
                    //if (keyAttr != null)
                    //{
                    //    info.IsKey = true;
                    //}
                    return info;
                }
            }
            return default;
        }

        /// <summary>
        /// 生成随机字母与数字
        /// </summary>
        /// <param name="Length">生成长度</param>
        /// <param name="Sleep">是否要在生成前将当前线程阻止以避免重复</param>
        /// <returns></returns>
        public static string Str(int Length, bool Sleep)
        {
            if (Sleep)
                System.Threading.Thread.Sleep(3);
            char[] Pattern = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
            string result = "";
            int n = Pattern.Length;
            Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
            for (int i = 0; i < Length; i++)
            {
                int rnd = random.Next(0, n);
                result += Pattern[rnd];
            }
            return string.IsNullOrEmpty(result) ? "a" : result;
        }

        public class StrFieldInfo
        {
            public bool IsKey { get; set; }
            public int Length { get; set; }
            public string Name { get; set; }
        }
    }
}
